package com.dynaBlaster.view;

import java.util.ArrayList;
import java.util.Random;

import com.dynaBlaster.R;
import com.dynaBlaster.R.drawable;
import com.dynaBlaster.R.string;
import com.dynaBlaster.core.Point;
import com.dynaBlaster.elements.Hero;
import com.dynaBlaster.game.Game;
import com.dynaBlaster.game.GameConfig;
import com.dynaBlaster.game.Terrain;
import com.dynaBlaster.game.Tile;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;


public class GameView extends BoardView implements SensorEventListener, OnTouchListener {

	public static final String LOG_TAG = "GameView";
	/**
	 * Current mode of application: READY to run, RUNNING, or you have already
	 * lost. static final ints are used instead of an enum for performance
	 * reasons.
	 */
	private int mMode = READY;
	public static final int PAUSE = 0;
	public static final int READY = 1;
	public static final int RUNNING = 2;
	public static final int GAME_LOST = 3;
	public static final int GAME_WON = 4;

	public int control_mode;
	public static final int CONTROL_TOUCH_AND_KEYBORD = 0;
	public static final int CONTROL_SENSOR_AND_KEYBORD = 1;



	/**
	 * mLastMove: tracks the absolute time when the snake last moved, and is
	 * used to determine if a move should be made based on mMoveDelay.
	 */
	private long mLastMove;

	/**
	 * mStatusText: text shows to the user in some run states
	 */
	private TextView mStatusText;

	/**
	 * 
	 * hero coordinates
	 */
	private static final int HERO_START_X = 1, HERO_START_Y = 1;

	private static final int exitX = HERO_START_X + 2, exitY = HERO_START_Y + 2;
	private Hero mHero;



	private Resources r = this.getContext().getResources();

	boolean upKeyPressed, downKeyPressed, leftKeyPressed, rightKeyPressed;

	boolean isHeroMoving = false;
	long tapStart;
	
	private Bitmap bombPlanting;


	/**
	 * Create a simple handler that we can use to cause animation to happen. We
	 * set ourselves as a target and we can use the sleep() function to cause an
	 * update/invalidate to occur at a later date.
	 */
	private RefreshHandler mRedrawHandler = new RefreshHandler();

	class RefreshHandler extends Handler {

		@Override
		public void handleMessage(Message msg) {
			GameView.this.update();
			GameView.this.invalidate();
		}

		public void sleep(long delayMillis) {
			this.removeMessages(0);
			sendMessageDelayed(obtainMessage(0), delayMillis);
		}
	};

	/**
	 * Constructs a SnakeView based on inflation from XML
	 * 
	 * @param context
	 * @param attrs
	 */
	public GameView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public Game game;
	
	public GameView(Context context, AttributeSet attrs, int defStyle, Game game) {
		super(context, attrs, defStyle, game.board);
		init();
		
		this.game = game;
	}

	private void init() {
		setFocusable(true);
		
		loadTile(Terrain.FREE, r.getDrawable(R.drawable.free));
		loadTile(Terrain.WALL, r.getDrawable(R.drawable.wall));
		loadTile(Terrain.CONCRETE, r.getDrawable(R.drawable.concrete));
		loadTile(Terrain.EXIT, r.getDrawable(R.drawable.exit));
		
		Drawable bomb=r.getDrawable(R.drawable.bomb);
		bombPlanting = Bitmap.createBitmap(GameConfig.PLANT_BOMB_SIZE,GameConfig.PLANT_BOMB_SIZE, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bombPlanting);
		bomb.setBounds(0, 0, GameConfig.PLANT_BOMB_SIZE, GameConfig.PLANT_BOMB_SIZE);
		bomb.draw(canvas);
	}

	private void initNewGame() {

		game.initNewGame();
		
		if (control_mode == CONTROL_SENSOR_AND_KEYBORD)
			initSensor();

	}




	@Override
	public void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		if (mMode == RUNNING || mMode == GAME_LOST || mMode == GAME_WON) {
			for (Bomb b : bombs) {
				b.draw(canvas);
			}

			mHero.draw(canvas);

			for (Creature c : monsters) {
				c.draw(canvas);
			}
			
			canvas.drawBitmap(bombPlanting, 0,mYTileCount*mTileSize - GameConfig.PLANT_BOMB_SIZE , new Paint());

		} else if (mMode == READY) {
			Paint paint = new Paint();
			paint.setColor(Color.GRAY);
			paint.setStrokeWidth(9);
			canvas.drawLine(0, 250, 350, 250, paint);
		}
	}



	@Override
	public boolean onKeyDown(int keyCode, KeyEvent msg) {

		if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
			if (mMode == READY || mMode == GAME_LOST || mMode == GAME_WON) {

				initNewGame();
				setMode(RUNNING);
				update();
				return (true);
			}

			if (mMode == PAUSE) {
				/*
				 * If the game is merely paused, we should just continue where
				 * we left off.
				 */
				setMode(RUNNING);
				update();
				return (true);
			}

			mHero.setDir(Creature.Direction.NORTH);
			// mHero.move();
			upKeyPressed = true;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
			mHero.setDir(Creature.Direction.SOUTH);
			// mHero.move();
			downKeyPressed = true;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
			mHero.setDir(Creature.Direction.WEST);
			// mHero.move();
			leftKeyPressed = true;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
			mHero.setDir(Creature.Direction.EAST);
			// mHero.move();
			rightKeyPressed = true;
			return (true);
		}
		if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SPACE) {

			plantBomb();

			return (true);
		}

		return super.onKeyDown(keyCode, msg);
	}



	@Override
	public boolean onKeyUp(int keyCode, KeyEvent msg) {
		if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
			upKeyPressed = false;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
			downKeyPressed = false;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
			leftKeyPressed = false;
			return (true);
		}

		if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
			rightKeyPressed = false;
			return (true);
		}

		return super.onKeyUp(keyCode, msg);
	}

	/**
	 * Sets the TextView that will be used to give information (such as "Game
	 * Over" to the user.
	 * 
	 * @param newView
	 */
	public void setTextView(TextView newView) {
		mStatusText = newView;
	}

	/**
	 * Updates the current mode of the application (RUNNING or PAUSED or the
	 * like) as well as sets the visibility of textview for notification
	 * 
	 * @param newMode
	 */
	public void setMode(int newMode) {
		int oldMode = mMode;
		mMode = newMode;

		if (newMode == RUNNING & oldMode != RUNNING) {
			mStatusText.setVisibility(View.INVISIBLE);
			update();
			return;
		}

		CharSequence str = "";
		if (newMode == PAUSE) {
			str = r.getText(R.string.mode_pause);
		}
		if (newMode == READY) {
			str = r.getText(R.string.mode_ready);
		}
		if (newMode == GAME_LOST) {
			str = r.getString(R.string.mode_lose);
		}
		if (newMode == GAME_WON) {
			str = r.getString(R.string.mode_won);
		}

		mStatusText.setText(str);
		mStatusText.setVisibility(View.VISIBLE);
	}

	/**
	 * Handles the basic update loop, checking to see if we are in the running
	 * state, determining if a move should be made, updating the snake's
	 * location.
	 */
	public void update() {
		if (mMode == RUNNING) {
			long now = System.currentTimeMillis();

			if (now - mLastMove > GameConfig.GAME_SPEED) {
				updateGame();
				mLastMove = now;
			}
			mRedrawHandler.sleep(GameConfig.GAME_SPEED);
		}

	}



	public Resources getR() {
		return r;
	}

	/**
	 * Save game state so that the user does not lose anything if the game
	 * process is killed while we are in the background.
	 * 
	 * @return a Bundle with this view's state
	 */
	public Bundle saveState() {
		Bundle map = new Bundle();

		// map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
		// map.putInt("mDirection", Integer.valueOf(mDirection));
		// map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
		// map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
		// map.putLong("mScore", Long.valueOf(mScore));
		// map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));

		return map;
	}

	/**
	 * Restore game state if our process is being relaunched
	 * 
	 * @param icicle
	 *            a Bundle containing the game state
	 */
	public void restoreState(Bundle icicle) {
		setMode(PAUSE);

		// mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
		// mDirection = icicle.getInt("mDirection");
		// mNextDirection = icicle.getInt("mNextDirection");
		// mMoveDelay = icicle.getLong("mMoveDelay");
		// mScore = icicle.getLong("mScore");
		// mSnakeTrail =
		// coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
	}


	
	@Override
	public boolean onTouch(View v, MotionEvent event) {

		logTouchEvent(event);

		if (mMode == GAME_LOST || mMode == GAME_WON) {
			initNewGame();
			setMode(RUNNING);
			update();
			return true;
		} else if (mMode == READY) {
			int y = (int) event.getY(0);

			if (y > 240) {
				control_mode = CONTROL_SENSOR_AND_KEYBORD;
			} else {
				control_mode = CONTROL_TOUCH_AND_KEYBORD;
			}

			initNewGame();
			setMode(RUNNING);
			update();
			return true;
		} else if (mMode == RUNNING && control_mode == CONTROL_TOUCH_AND_KEYBORD) {

			Point h = mHero.getPos();
			int x = (int) event.getX(0);
			int y = (int) event.getY(0);

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:// start moving
				if (isInPlantbombArea(x,y)){
					plantBomb();
					return true;
				}
				isHeroMoving = true;
				mHero.setHeroDirections(resolveDirByTapCoord(h, x, y));
				tapStart = System.currentTimeMillis();
				break;
			case MotionEvent.ACTION_MOVE: // change direction
				mHero.setHeroDirections(resolveDirByTapCoord(h, x, y));
				break;
			case MotionEvent.ACTION_UP: // end moving
				isHeroMoving = false;
				break;
			default:
				break;
			}
	 

			return true;
		} else if (mMode == RUNNING && control_mode == CONTROL_SENSOR_AND_KEYBORD) {

			Point h = mHero.getPos();
			int x = (int) event.getX(0);
			int y = (int) event.getY(0);

			if (event.getAction() == MotionEvent.ACTION_DOWN) { // end moving

				plantBomb();
			}

			return true;
		}
		return false; // indicate event was handled
	}


	/** Show an event in the LogCat view, for debugging */
	private void logTouchEvent(MotionEvent event) {

		String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?",
				"8?", "9?" };
		StringBuilder sb = new StringBuilder();
		int action = event.getAction();
		int actionCode = action & MotionEvent.ACTION_MASK;
		sb.append("TOUCH event ACTION_").append(names[actionCode]);
		if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
			sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
			sb.append(")");
		}
		sb.append("[");
		for (int i = 0; i < event.getPointerCount(); i++) {
			sb.append("#").append(i);
			sb.append("(pid ").append(event.getPointerId(i));
			sb.append(")=").append((int) event.getX(i));
			sb.append(",").append((int) event.getY(i));
			if (i + 1 < event.getPointerCount())
				sb.append(";");
		}
		sb.append("]");
		Log.d(LOG_TAG, sb.toString());

	}

	com.com.dynaBlaster.core.Direction[] resolveDirByTapCoord(Point pos, int tapX, int tapY) {

		Creature.Direction primaryDir = null;
		Creature.Direction secondaryDir = null;

		int xDiff = tapX - pos.x;
		int yDiff = tapY - pos.y;

		int xMod = Math.abs(xDiff);
		int yMod = Math.abs(yDiff);

		if (xMod <= yMod && yDiff >= 0) {
			primaryDir = Creature.Direction.SOUTH;
			if (xDiff >= 0) {
				secondaryDir = Creature.Direction.EAST;
			} else {
				secondaryDir = Creature.Direction.WEST;
			}
		} else if (xMod <= yMod && yDiff < 0) {
			primaryDir = Creature.Direction.NORTH;
			if (xDiff >= 0) {
				secondaryDir = Creature.Direction.EAST;
			} else {
				secondaryDir = Creature.Direction.WEST;
			}
		} else if (xMod > yMod && xDiff < 0) {
			primaryDir = Creature.Direction.WEST;
			if (yDiff >= 0) {
				secondaryDir = Creature.Direction.SOUTH;
			} else {
				secondaryDir = Creature.Direction.NORTH;
			}
		} else if (xMod > yMod && xDiff >= 0) {
			primaryDir = Creature.Direction.EAST;
			if (yDiff >= 0) {
				secondaryDir = Creature.Direction.SOUTH;
			} else {
				secondaryDir = Creature.Direction.NORTH;
			}
		}

		return new Creature.Direction[] { primaryDir, secondaryDir };
	}



	private SensorManager mSensorManager;
	private Sensor mAccelerometer;
	private Sensor mOrientation;

	public void initSensor() {
		mSensorManager = (SensorManager) this.getContext().getSystemService(Context.SENSOR_SERVICE);
		mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		mOrientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
		resumeSensor();
	}

	protected void resumeSensor() {
		mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
		mSensorManager.registerListener(this, mOrientation, SensorManager.SENSOR_DELAY_NORMAL);
	}

	protected void pauseSensor() {
		mSensorManager.unregisterListener(this);
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// Log.d(LOG_TAG,"onAccuracyChanged: " + sensor + ", accuracy: " +
		// accuracy);

	}

	@Override
	public void onSensorChanged(SensorEvent event) {

		if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
			float[] val = event.values;
			Log.d(LOG_TAG, "========================================");
			Log.d(LOG_TAG, "SENSOR Orientation X ");
			Log.d(LOG_TAG, "SENSOR Orientation Y ");
			Log.d(LOG_TAG, "SENSOR Orientation Z ");
			Log.d(LOG_TAG, "SENSOR X ");
			Log.d(LOG_TAG, "SENSOR Y ");
			Log.d(LOG_TAG, "SENSOR Z ");
			Log.d(LOG_TAG, "		SENSOR ACC X " + val[0]);
			Log.d(LOG_TAG, "		SENSOR ACC Y " + val[1]);
			Log.d(LOG_TAG, "		SENSOR ACC Z " + val[2]);
			Log.d(LOG_TAG, "========================================");

		} else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
			float[] val = event.values;
			Log.d(LOG_TAG, "========================================");
			Log.d(LOG_TAG, "SENSOR Orientation X " + (int) val[0]);
			Log.d(LOG_TAG, "SENSOR Orientation Y " + (int) val[1]);
			Log.d(LOG_TAG, "SENSOR Orientation Z " + (int) val[2]);
			Log.d(LOG_TAG, "SENSOR X ");
			Log.d(LOG_TAG, "SENSOR Y ");
			Log.d(LOG_TAG, "SENSOR Z ");
			Log.d(LOG_TAG, "		SENSOR ACC X ");
			Log.d(LOG_TAG, "		SENSOR ACC Y ");
			Log.d(LOG_TAG, "		SENSOR ACC Z ");
			Log.d(LOG_TAG, "========================================");
			mHero.setDir(resolveDirSensorCoord(val[0], val[1], val[2]));
		} else {
			// Log.d(LOG_TAG, "SENSOR other event:"+event.sensor.getType());
			float[] val = event.values;
			Log.d(LOG_TAG, "========================================");
			Log.d(LOG_TAG, "SENSOR Orientation X ");
			Log.d(LOG_TAG, "SENSOR Orientation Y ");
			Log.d(LOG_TAG, "SENSOR Orientation Z ");
			Log.d(LOG_TAG, "SENSOR X " + val[0]);
			Log.d(LOG_TAG, "SENSOR Y " + val[1]);
			Log.d(LOG_TAG, "SENSOR Z " + val[2]);
			Log.d(LOG_TAG, "		SENSOR ACC X ");
			Log.d(LOG_TAG, "		SENSOR ACC Y ");
			Log.d(LOG_TAG, "		SENSOR ACC Z ");
			Log.d(LOG_TAG, "========================================");
		}
	}

	com.com.dynaBlaster.core.Direction resolveDirSensorCoord(float sensX, float sensY, float sensZ) {

		Creature.Direction ret = Creature.Direction.SOUTH;

		if (sensY < -GameConfig.MIN_ANGLE_TO_PERFORM_TURN) {
			isHeroMoving = true;
			ret = Creature.Direction.SOUTH;
		} else if (sensY > GameConfig.MIN_ANGLE_TO_PERFORM_TURN) {
			isHeroMoving = true;
			ret = Creature.Direction.NORTH;
		} else if (sensZ > GameConfig.MIN_ANGLE_TO_PERFORM_TURN) {
			isHeroMoving = true;
			ret = Creature.Direction.WEST;
		} else if (sensZ < -GameConfig.MIN_ANGLE_TO_PERFORM_TURN) {
			isHeroMoving = true;
			ret = Creature.Direction.EAST;
		} else {
			isHeroMoving = false;
		}

		return ret;
	}

}
